﻿using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;

namespace Bunbyx.game
{
    public class Camera
    {
        // --------------------------------------------------------------------
        //  Constants
        // --------------------------------------------------------------------

        private const float FOLLOW_MAX_DELTA_X = 400;
        private const float FOLLOW_MAX_DELTA_Y = 100;

        // --------------------------------------------------------------------
        //  Public attributes
        // --------------------------------------------------------------------

        public Matrix View
        {
            get;
            private set;
        }

        public Vector2 Position
        {
            get
            {
                return cameraPosition;
            }
        }

        public Rectangle Container;

        // --------------------------------------------------------------------
        //  Private attributes
        // --------------------------------------------------------------------

        private Vector2 cameraPosition;
        private Vector2 screenCenter;
        private Viewport viewport;

        // --------------------------------------------------------------------
        //  Constructor
        // --------------------------------------------------------------------

        public Camera(Viewport viewport)
        {
            this.viewport = viewport;
            this.View = Matrix.Identity;
            this.screenCenter = new Vector2(viewport.Width / 2, viewport.Height / 2);

            Reset();
        }

        public void Reset()
        {
            this.cameraPosition = Vector2.Zero;

            updateView();
        }

        public void CenterOn(Sprite sprite)
        {
            cameraPosition.X = sprite.Position.X - screenCenter.X + sprite.Size.Width / 2;
            cameraPosition.Y = sprite.Position.Y - screenCenter.Y + sprite.Size.Height / 2;

            restrainView();
            updateView();
        }

        public void Follow(Sprite sprite)
        {
            float dx = sprite.Position.X - cameraPosition.X;
            float dy = sprite.Position.Y - cameraPosition.Y;

            // Follow left
            if (dx < FOLLOW_MAX_DELTA_X)
            {
                cameraPosition.X = sprite.Position.X - FOLLOW_MAX_DELTA_X;
            }
            // Follow right
            else if (dx + sprite.Size.Width > viewport.Width - FOLLOW_MAX_DELTA_X)
            {
                cameraPosition.X = sprite.Position.X - viewport.Width + sprite.Size.Width + FOLLOW_MAX_DELTA_X;
            }

            // Follow up
            if (dy < FOLLOW_MAX_DELTA_Y)
            {
                cameraPosition.Y = sprite.Position.Y - FOLLOW_MAX_DELTA_Y;
            }
            // Follow down
            else if (dy + sprite.Size.Height > viewport.Height - FOLLOW_MAX_DELTA_Y)
            {
                cameraPosition.Y = sprite.Position.Y - viewport.Height + sprite.Size.Height + FOLLOW_MAX_DELTA_Y;
            }

            restrainView();
            updateView();
        }

        // --------------------------------------------------------------------
        //  Private methods
        // --------------------------------------------------------------------

        private void restrainView()
        {
            float x1 = cameraPosition.X;
            float x2 = x1 + viewport.Width;
            float y1 = cameraPosition.Y;
            float y2 = y1 + viewport.Height;

            float cx1 = Container.X;
            float cx2 = cx1 + Container.Width;
            float cy1 = Container.Y;
            float cy2 = cy1 + Container.Height;

            // Restrain on X
            if (x1 < cx1)
            {
                cameraPosition.X = cx1;
            }
            else if (x2 > cx2)
            {
                cameraPosition.X = cx2 - viewport.Width;
            }

            // Restrain on Y
            if (y1 < cy1)
            {
                cameraPosition.Y = cy1;
            }
            else if (y2 > cy2)
            {
                cameraPosition.Y = cy2 - viewport.Height;
            }
        }

        private void updateView()
        {
            View = Matrix.CreateTranslation(new Vector3(-cameraPosition - screenCenter, 0f)) *
                   Matrix.CreateTranslation(new Vector3(screenCenter, 0f));
        }
    }
}
